package de.ff7rl.core.tools;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.List;

import javax.swing.AbstractListModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreeSelectionModel;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;

import com.thoughtworks.xstream.XStream;

import de.ff7rl.core.GameInfo;
import de.ff7rl.core.effects.Effect;
import de.ff7rl.core.effects.EffectType;
import de.ff7rl.core.effects.Item;
import de.ff7rl.core.effects.Target;
import de.ff7rl.core.effects.TargetSize;
import de.ff7rl.core.reference.WeakReference;

public class ItemEditor extends JFrame implements TreeSelectionListener, ListSelectionListener {

  private static final XStream XSTREAM = new XStream();
  private static final Item NULL_ITEM = new Item() {
    @Override
    public String getDescription() {
      return StringUtils.EMPTY;
    }

    @Override
    public List<Effect> getEffects() {
      return Collections.EMPTY_LIST;
    }

    @Override
    public String getName() {
      return StringUtils.EMPTY;
    }
  };
  private final static File ITEM_ROOT = new File(new File(System.getProperty("user.dir"),
      "src/main/resources/de/ff7rl/core"), "items");

  public static void main(final String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        try {
          new ItemEditor();
        } catch (final IOException e) {
          e.printStackTrace();
        }
      }
    });
  }

  protected static String getFileName(final Item theItem) {
    return String.format("%s.item", WeakReference.toFileName(theItem.getName()));
  }

  private JTree itemTree;
  private JTextField nameTextField;
  private JTextField descriptionTextField;
  private Item itemInView = NULL_ITEM;
  private JList effectsList;
  private JList effectAgumentsList;
  private JComboBox defaultTargetComboBox;
  private JComboBox targetSizeComboBox;

  public ItemEditor() throws IOException {
    super(String.format("%s ItemEditor %s", GameInfo.getName(), GameInfo.getVersion()));
    initializeView();
    updateView(false);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void valueChanged(final ListSelectionEvent theEvent) {
    final int effectIndex = effectsList.getSelectedIndex();
    if (effectIndex == -1) {
      effectAgumentsList.setModel(new AbstractListModel() {
        @Override
        public Object getElementAt(final int theIndex) {
          return null;
        }

        @Override
        public int getSize() {
          return 0;
        }
      });
    } else {
      final Effect effect = itemInView.getEffects().get(effectIndex);
      effectAgumentsList.setModel(new AbstractListModel() {
        @Override
        public Object getElementAt(final int theIndex) {
          return effect.getArguments().get(theIndex);
        }

        @Override
        public int getSize() {
          return effect.getArguments().size();
        }
      });
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void valueChanged(final TreeSelectionEvent theEvent) {
    final DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) theEvent.getPath().getLastPathComponent();
    final Object userObject = selectedNode.getUserObject();
    if (userObject instanceof Item) {
      itemInView = (Item) userObject;
    } else {
      itemInView = NULL_ITEM;
    }
    updateView(false);
  }

  private void initializeView() throws IOException {
    setSize(800, 600);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setLocationRelativeTo(null);
    setVisible(true);
    setLayout(new BorderLayout());
    final JMenuBar menuBar = new JMenuBar();
    final JMenu menu = new JMenu("Editor");
    final JMenuItem createNewItemMenuItem = new JMenuItem("new Item");
    createNewItemMenuItem.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent theE) {
        final String itemName = JOptionPane.showInputDialog(null, "Enter new item name:", "New Item",
            JOptionPane.QUESTION_MESSAGE);
        if (itemName == null) {
          return;
        }
        final Item newItem = new Item();
        newItem.setName(itemName);
        itemInView = newItem;
        saveCurrentItem();
        updateView(true);
      }
    });
    menu.add(createNewItemMenuItem);
    menuBar.add(menu);
    setJMenuBar(menuBar);
    itemTree = new JTree(loadItemsForTree());
    itemTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
    itemTree.getSelectionModel().addTreeSelectionListener(this);
    itemTree.addKeyListener(new KeyListener() {
      @Override
      public void keyPressed(final KeyEvent theEvent) {
        if (theEvent.getKeyCode() != KeyEvent.VK_DELETE) {
          return;
        }
        final Object userObject = ((DefaultMutableTreeNode) itemTree.getSelectionPath().getLastPathComponent())
            .getUserObject();
        if (!(userObject instanceof Item)) {
          return;
        }
        final Item item = (Item) userObject;
        final String itemFileName = getFileName(item);
        final File itemFile = new File(ITEM_ROOT, itemFileName);
        FileUtils.deleteQuietly(itemFile);
        itemInView = NULL_ITEM;
        updateView(true);
      }

      @Override
      public void keyReleased(final KeyEvent theEvent) {
      }

      @Override
      public void keyTyped(final KeyEvent theEvent) {
      }
    });
    add(new JScrollPane(itemTree), BorderLayout.WEST);
    final JPanel panel = new JPanel(new VerticalLayout(5, VerticalLayout.BOTH, VerticalLayout.TOP));
    add(panel, BorderLayout.CENTER);
    final JPanel namePanel = new JPanel(new BorderLayout());
    namePanel.setBorder(new TitledBorder("Name:"));
    nameTextField = new JTextField();
    nameTextField.setEditable(false);
    namePanel.add(nameTextField, BorderLayout.CENTER);
    final JPanel descriptionPanel = new JPanel(new BorderLayout());
    descriptionPanel.setBorder(new TitledBorder("Description:"));
    descriptionTextField = new JTextField();
    descriptionTextField.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent theEvent) {
        if (itemInView == NULL_ITEM) {
          return;
        }
        itemInView.setDescription(descriptionTextField.getText());
        saveCurrentItem();
        updateView(true);
      }
    });
    descriptionPanel.add(descriptionTextField, BorderLayout.CENTER);
    final JPanel targetPanel = new JPanel(new BorderLayout());
    targetPanel.setBorder(new TitledBorder("Default target:"));
    defaultTargetComboBox = new JComboBox(Target.values());
    defaultTargetComboBox.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent theEvent) {
        if (itemInView == NULL_ITEM) {
          return;
        }
        itemInView.setDefaultTarget((Target) defaultTargetComboBox.getSelectedItem());
        saveCurrentItem();
        updateView(false);
      }
    });
    targetPanel.add(defaultTargetComboBox, BorderLayout.CENTER);
    final JPanel targetSizePanel = new JPanel(new BorderLayout());
    targetSizePanel.setBorder(new TitledBorder("Target size:"));
    targetSizeComboBox = new JComboBox(TargetSize.values());
    targetSizeComboBox.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent theEvent) {
        if (itemInView == NULL_ITEM) {
          return;
        }
        itemInView.setTargetSize((TargetSize) targetSizeComboBox.getSelectedItem());
        saveCurrentItem();
        updateView(false);
      }
    });
    targetSizePanel.add(targetSizeComboBox, BorderLayout.CENTER);
    final JPanel effectsPanel = new JPanel(new VerticalLayout(5, VerticalLayout.BOTH, VerticalLayout.TOP));
    effectsPanel.setBorder(new TitledBorder("Effects:"));
    effectsList = new JList();
    effectsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    effectsList.addKeyListener(new KeyListener() {
      @Override
      public void keyPressed(final KeyEvent theEvent) {
        if (theEvent.getKeyCode() != KeyEvent.VK_DELETE) {
          return;
        }
        final int effectIndex = effectsList.getSelectedIndex();
        if (effectIndex == -1) {
          return;
        }
        if (itemInView.getEffects().isEmpty()) {
          return;
        }
        itemInView.getEffects().remove(effectIndex);
        saveCurrentItem();
        updateView(true);
      }

      @Override
      public void keyReleased(final KeyEvent theEvent) {
      }

      @Override
      public void keyTyped(final KeyEvent theEvent) {
      }
    });
    effectsList.getSelectionModel().addListSelectionListener(this);
    effectsPanel.add(new JScrollPane(effectsList));
    final Object[] comboBoxEntries = new Object[EffectType.values().length + 1];
    comboBoxEntries[0] = "add Effect...";
    System.arraycopy(EffectType.values(), 0, comboBoxEntries, 1, EffectType.values().length);
    final JComboBox effectsComboBox = new JComboBox(comboBoxEntries);
    effectsComboBox.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent theEvent) {
        final Object selectedItem = effectsComboBox.getSelectedItem();
        if (selectedItem == null || !(selectedItem instanceof EffectType)) {
          return;
        }
        final EffectType effectType = (EffectType) selectedItem;
        final Effect effect = new Effect();
        effect.setType(effectType);
        itemInView.getEffects().add(effect);
        saveCurrentItem();
        updateView(true);
        effectsComboBox.setSelectedIndex(0);
      }
    });
    effectsPanel.add(effectsComboBox);
    effectAgumentsList = new JList();
    effectAgumentsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    effectAgumentsList.addKeyListener(new KeyListener() {
      @Override
      public void keyPressed(final KeyEvent theEvent) {
        if (theEvent.getKeyCode() != KeyEvent.VK_DELETE) {
          return;
        }
        final int argumentIndex = effectAgumentsList.getSelectedIndex();
        if (argumentIndex == -1) {
          return;
        }
        final Effect effect = itemInView.getEffects().get(effectsList.getSelectedIndex());
        if (effect.getArguments().isEmpty()) {
          return;
        }
        effect.getArguments().remove(argumentIndex);
        saveCurrentItem();
        updateView(true);
      }

      @Override
      public void keyReleased(final KeyEvent theEvent) {
      }

      @Override
      public void keyTyped(final KeyEvent theEvent) {
      }
    });
    effectsPanel.add(new JScrollPane(effectAgumentsList));
    final JTextField effectArgumentTextField = new JTextField();
    effectArgumentTextField.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent theEvent) {
        final int effectIndex = effectsList.getSelectedIndex();
        if (effectIndex == -1) {
          return;
        }
        final Effect effect = itemInView.getEffects().get(effectIndex);
        effect.getArguments().add(effectArgumentTextField.getText());
        saveCurrentItem();
        updateView(true);
        effectArgumentTextField.setText(StringUtils.EMPTY);
      }
    });
    effectsPanel.add(effectArgumentTextField);
    panel.add(namePanel);
    panel.add(descriptionPanel);
    panel.add(targetPanel);
    panel.add(targetSizePanel);
    panel.add(effectsPanel);
    pack();
  }

  private TreeNode loadItemsForTree() {
    final DefaultMutableTreeNode root = new DefaultMutableTreeNode("Items");
    for (final File itemFile : FileUtils.listFiles(ITEM_ROOT, new String[] { "item" }, false)) {
      try {
        final Item item = (Item) XSTREAM.fromXML(new FileInputStream(itemFile));
        root.add(new DefaultMutableTreeNode(item));
      } catch (final FileNotFoundException e) {
        e.printStackTrace();
      }
    }
    return root;
  }

  private void saveCurrentItem() {
    if (itemInView == NULL_ITEM) {
      return;
    }
    final String itemFileName = getFileName(itemInView);
    final File itemFile = new File(ITEM_ROOT, itemFileName);
    try {
      XSTREAM.toXML(itemInView, new FileOutputStream(itemFile));
      updateView(true);
    } catch (final FileNotFoundException e) {
      e.printStackTrace();
    }
  }

  private void updateView(final boolean theReloadTree) {
    if (theReloadTree) {
      ((DefaultTreeModel) itemTree.getModel()).setRoot(loadItemsForTree());
    }
    itemTree.expandRow(0);
    nameTextField.setText(itemInView.getName());
    descriptionTextField.setText(itemInView.getDescription());
    targetSizeComboBox.setSelectedItem(itemInView.getTargetSize());
    defaultTargetComboBox.setSelectedItem(itemInView.getDefaultTarget());
    effectsList.setModel(new AbstractListModel() {
      @Override
      public Object getElementAt(final int theIndex) {
        return itemInView.getEffects().get(theIndex).getType();
      }

      @Override
      public int getSize() {
        return itemInView.getEffects().size();
      }
    });
  }
}
